<dl class="archive">
    <dt><a href="#">奇异的 arguments（翻译）</a></dt>
    <dd>
    <p class="date"><span>2008-11-18 19:56:36</span></p>
    <ul>
        <li>原标题：arguments: A JavaScript Oddity</li>
        <li>原作者：<a title="http://www.sitepoint.com/articlelist/487" href="http://www.sitepoint.com/articlelist/487">Andrew Tetlaw</a></li>
        <li>原连接：<a title="http://www.sitepoint.com/blogs/2008/11/11/arguments-a-javascript-oddity/" href="http://www.sitepoint.com/blogs/2008/11/11/arguments-a-javascript-oddity/">http://www.sitepoint.com/blogs/2008/11/11/arguments-a-javascript-oddity/</a></li>
    </ul>

    <p>在 Javascript 的函数中有个名为 arguments 的类数组对象。它看起来是那么的诡异而且名不经传，但众多的 Javascript 库都使用着它强大的功能。所以，它的特性需要每个 Javascript 程序员去熟悉它。</p>

    <p>在每个函数中，都有个名为 arguments 的变量，它以类似数组的形式保存了当前调用的参数。而它实际上并不是个数组，使用 typeof arguments 语句尝试会返回“object”（对象），所以它不能像 Array 一样使用 push 和 pop 等方法。即便如此，仍然可以使用下标以及长度属性（length）获取它的值。</p>

    <h2>编写灵活的函数</h2><p>虽看起来名不经传，但的确 arguments 是非常有用的对象。比如，你可以让函数处理不定数目的参数。在 Dean Edwards 写的 <a title="http://base2.googlecode.com/" href="http://base2.googlecode.com/">base2 库</a>中，有个叫 format 的函数充分发挥了这一特性：</p>

<pre>function format(string) {
  var args = arguments;
  var pattern = new RegExp("%([1-" + arguments.length + "])", "g");
  return String(string).replace(pattern, function(match, index) {
    return args[index];
  });
};</pre><p>这个函数实现了模板替换，你可以在要动态替换的地方使用 %1 到 %9 标记，然后其余的参数就会依次替换这些地方。例如：</p>

<pre>format("And the %1 want to know whose %2 you %3", "papers", "shirt", "wear");</pre><p>上面的脚本就会返回</p>

<pre>"And the papers want to know whose shirt you wear" 。</pre><p>在这里需要注意的是，即便在 format 函数定义中，我们仅定义了个名为 string 的参数。而 Javascript 不管函数自身定义的参数数量，它都允许我们向一个函数传递任意数量的参数，并将这些参数值保存到被调用函数的 arguments 对象中。</p>

<h3>转换成实际数组</h3><p>虽然 arguments 对象并不是真正意义上的 Javascript 数组，但是我们可以使用数组的 slice 方法将其转换成数组，类似下面的代码</p>

<pre>var args = Array.prototype.slice.call(arguments);</pre><p>这样，数组变量 args 包含了所有 arguments 对象包含的值。</p>

<h2>创建预置参数的函数</h2><p>使用 arguments 对象能够简短我们编写的 Javascript 代码量。下面有个名为 makeFunc 的函数，它根据你提供的函数名称以及其他任意数目的参数，然后返回个匿名函数。此匿名函数被调用时，合并的原先被调用的参数，并交给指定的函数运行然后返回其返回值。</p>

<pre>function makeFunc() {
  var args = Array.prototype.slice.call(arguments);
  var func = args.shift();
  return function() {
    return func.apply(null, args.concat(Array.prototype.slice.call(arguments)));
  };
}</pre><p>makeFunc 的第一个参数指定需要调用的函数名称（是的，在这个简单的例子中没有错误检查），获取以后从 args 中删除。makeFunc 返回一个匿名函数，它使用函数对象的（Function Object）apply 方法调用指定的函数。</p>

<p>apply 方法的第一个参数指定了作用域，基本上的作用域是被调用的函数。不过这样在这个例子中看起来会有点复杂，所以我们将其设定成 null ；其第二个参数是个数组，它指定了其调用函数的参数。makeFunc 转换其自身的 arguments 并连接匿名函数的 arguments，然后传递到被调用的函数。</p>

<p>有种情况就是总是要有个输出的模板是相同的，为了节省每次是使用上面提到的 format 函数并指定重复的参数，我们可以使用 makeFunc 这个工具。它将返回一个匿名函数，并自动生成已经指定模板后的内容：</p>

<pre>var majorTom = makeFunc(format, "This is Major Tom to ground control. I'm %1.");</pre><p>你可以像这样重复指定 majorTom 函数：</p>

<pre>majorTom("stepping through the door");
majorTom("floating in a most peculiar way");</pre><p>那么当每次调用 majorTom 函数时，它都会使用第一个指定的参数填写已经指定的模板。例如上述的代码返回：</p>

<pre>"This is Major Tom to ground control. I'm stepping through the door."
"This is Major Tom to ground control. I'm floating in a most peculiar way."</pre><h2>自引用的函数</h2><p>您可能会认为这很酷，先别急着高兴，后面还有个更大的惊喜。它（arguments）还有个其他非常有用的属性：callee 。arguments.callee 包含了当前调用函数的被引用对象。那么我们如何使用这玩意做些的事情？arguments.callee 是个非常有用的调用自身的匿名函数。</p>

<p>下面有个名为 repeat 的函数，它的参数需要个函数引用和两个数字。第一个数字表示运行的次数，而第二个函数定义运行的间隔时间（毫秒为单位）。下面是相关的代码：</p>

<pre>function repeat(fn, times, delay) {
  return function() {
    if(times-- > 0) {
      fn.apply(null, arguments);
      var args = Array.prototype.slice.call(arguments);
      var self = arguments.callee;
      setTimeout(function(){self.apply(null,args)}, delay);
    }
  };
}</pre>
<p>repeat 函数使用 arguments.callee 获得当前引用，保存到 self 变量后，返回个匿名函数重新运行原本被调用的函数。最后使用 setTimeout 以及配合个匿名函数实现延迟执行。</p>

<p>作为个简单的说明，比如会在通常的脚本中，编写下面的提供个字符串并弹出个警告框的简单函数：</p>

<pre>function comms(s) {
  alert(s);
}</pre>
<p>好了，后来我改变了我的想法。我想编写个“特殊版本”的函数，它会重复三次运行每次间隔两秒。那么使用我的 repeat 函数，就可以像这样做到：</p>

<pre>var somethingWrong = repeat(comms, 3, 2000);
somethingWrong("Can you hear me, major tom?");</pre><p>结果就犹如预期的那样，弹出了三次警告框每次延时两秒。</p>

<p>最后，arguments 即便不会经常被用到，甚至显得有些诡异，但是它上述的那些惊艳的功能（不仅仅是这些！）值得你去了解它。</p>

<p class="meta"><span><a href="#">永久连接</a> / <a
        class="content-type" href="/notes">Javascript</a> / <a href="#">现有 3 条回复</a></span></p>
</dd>
</dl>
